#include"GLSLProgram.h"
#include <fstream>
#include <iostream>

using namespace std;
GLSLProgram::GLSLProgram()
{
}

GLSLProgram::~GLSLProgram()
{
	if(buffer)
		delete buffer;
}


bool GLSLProgram::MakeProgram(char *vertexsh, char *fragmentsh, char *geometrysh )
{
	GLuint vertexglsl, fragmentglsl, geometryglsl;
	vertexglsl = glCreateShader(GL_VERTEX_SHADER);
	fragmentglsl = glCreateShader(GL_FRAGMENT_SHADER);
	geometryglsl = glCreateShader(GL_GEOMETRY_SHADER_EXT);

	//TODO make the length of the char adapted to the length of the shader source file
	buffer = new GLchar[100000];
	for(int i = 0; i< 100000; i++)
		buffer[i] = ' ';
	_loadshader(vertexsh, buffer);
	glShaderSource(vertexglsl,1,(GLchar const**)&buffer,&len);
	glCompileShader(vertexglsl);
	_printlog(vertexglsl);
	cout << "\nvertexshader compiled!\n" << endl;

	if (fragmentsh)
	{
		for(int i = 0; i< 100000; i++)
			buffer[i] = ' ';
		_loadshader(fragmentsh, buffer);//ftransf.glsl
		glShaderSource(fragmentglsl,1,(GLchar const**)&buffer,&len);
		glCompileShader(fragmentglsl);
		_printlog(fragmentglsl);
		cout << "fragmentshader compiled!\n" << endl;
	}

	if (geometrysh)
	{
		for(int i = 0; i< 100000; i++)
			buffer[i] = ' ';
		_loadshader(geometrysh, buffer);
		glShaderSource(geometryglsl,1,(GLchar const**)&buffer,&len);
		glCompileShader(geometryglsl);
		_printlog(geometryglsl);
		cout << "geometry shader compiled!\n" << endl;
	}

	cout <<"-------------"<<endl;

	delete[] buffer;
	GLint vertcompiled, fragmentcompiled, linked;
	glGetShaderiv(vertexglsl,GL_COMPILE_STATUS,&vertcompiled);
	glGetShaderiv(fragmentglsl,GL_COMPILE_STATUS,&fragmentcompiled);

	if(!vertcompiled)
		return false;

	//create program
	prog = glCreateProgram();

	//Setup GS
	//glProgramParameteriEXT(prog, GL_GEOMETRY_VERTICES_OUT_EXT, 1000);
	//glProgramParameteriEXT(prog, GL_GEOMETRY_INPUT_TYPE_EXT, GL_POINTS);
	//glProgramParameteriEXT(prog, GL_GEOMETRY_OUTPUT_TYPE_EXT, GL_TRIANGLE_STRIP);

	//Attach Shader
	glAttachShader(prog, vertexglsl);
	if(fragmentsh)
	glAttachShader(prog, fragmentglsl);
	if(geometrysh)
	glAttachShader(prog, geometryglsl);


	//Link program
	glLinkProgram(prog);

	//get program info
	glGetProgramiv(prog, GL_LINK_STATUS, &linked);
	_printlog(prog);

	if(!linked)
	{
		cout << "Link failed! \n"<< endl;
		return false;
	}


	return true;

}

void GLSLProgram::BindTexture(GLenum unit, GLenum texturetype, GLuint textureid, char* nameinshader)
{
	glActiveTexture(unit);
	glBindTexture(texturetype, textureid);
	GLuint loc = glGetUniformLocation(prog, nameinshader);
	glUniform1i(loc,unit-GL_TEXTURE0);	

}

void GLSLProgram::setUniform1f(char* nameinshader, float value)
{
	GLuint loc = glGetUniformLocation(prog, nameinshader);
	glUniform1f(loc, value);

}

void GLSLProgram::setUniform3f(char* nameinshader, float* value)
{
	GLuint loc = glGetUniformLocation(prog, nameinshader);
	glUniform3f(loc, value[0], value[1], value[2]);

}

void GLSLProgram::setUniformMatf(char* nameinshader, float* value)
{
	GLuint loc = glGetUniformLocation(prog, nameinshader);
	glUniformMatrix4fv(loc, 1, 0, value);

}

void GLSLProgram::setUniform1uint(char* nameinshader, unsigned int value)
{
	GLuint loc = glGetUniformLocation(prog, nameinshader);
	glUniform1uiEXT(loc, value);
}


void GLSLProgram::BeginProgram()
{
	glUseProgram(prog);
}

void GLSLProgram::EndProgram()
{
	glUseProgram(0);
}

//==========private member===========

void GLSLProgram::_loadshader(char *filename,char* buffer)
{


	ifstream shader;
	shader.open(filename, ios::in);
	shader.seekg(0,ios::end);
	len = int(shader.tellg());
	shader.seekg(0, ios::beg);

	shader.read(buffer,len);
	shader.close();

}

void GLSLProgram::_printlog(GLuint obj)
{
	int infologLength = 0;
	int maxLength;

	if(glIsShader(obj))
		glGetShaderiv(obj,GL_INFO_LOG_LENGTH,&maxLength);
	else
		glGetProgramiv(obj,GL_INFO_LOG_LENGTH,&maxLength);

	char *infoLog = new char[maxLength];

	if (glIsShader(obj))
		glGetShaderInfoLog(obj, maxLength, &infologLength, infoLog);
	else
		glGetProgramInfoLog(obj, maxLength, &infologLength, infoLog);

	if (infologLength > 0)
		printf("%s\n",infoLog);
}
